package sis.studentinfo;

import static org.junit.Assert.*;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

import org.junit.Before;
import org.junit.Test;

import sis.security.Permission;

public class AccountFactoryTest {

	private List<Method> updateMethods;
	private List<Method> readOnlyMethods;
	
	@Before
	public void init() throws SecurityException, NoSuchMethodException {
		updateMethods = new ArrayList<Method>();
		addUpdateMethod("setBankAba", String.class);
		addUpdateMethod("setBankAccountNumber", String.class);
		addUpdateMethod("setBankAccountType", Account.BankAccountType.class);
		addUpdateMethod("transferFromBank", BigDecimal.class);
		
		readOnlyMethods = new ArrayList<Method>();
		addReadOnlyMethod("getBalance");
		addReadOnlyMethod("transactionAverage");
	}

	@SuppressWarnings("rawtypes")
	private void addReadOnlyMethod(String name) throws SecurityException, NoSuchMethodException {
		Class[] noParms = new Class[]{};
		readOnlyMethods.add(Accountable.class.getDeclaredMethod(name, noParms));
	}

	@SuppressWarnings("rawtypes")
	private void addUpdateMethod(String name, Class parmClass) throws SecurityException, NoSuchMethodException {
		updateMethods.add(Accountable.class.getDeclaredMethod(name, parmClass));
	}
	
	@Test
	public void testUpdateAccess() throws Exception {
		Accountable account = AccountFactory.create(Permission.UPDATE);
		for(Method method : readOnlyMethods) {
			verifyNoException(method, account);
		}
		
		for(Method method : updateMethods) {
			verifyNoException(method, account);
		}
	}
	
	private void verifyNoException(Method method, Accountable account) throws Exception {
		try {
			method.invoke(account, nullParmsFor(method));
		} catch (InvocationTargetException e) {
			assertFalse("unexpected permission exception", 
					Permissionexception.class == e.getCause().getClass());
		}
	}

	@Test
	public void testReadOnlyAccess() throws Exception {
		Accountable account = AccountFactory.create(Permission.READ_ONLY);
		for(Method method : readOnlyMethods) {
			verifyException(Permissionexception.class, method, account);
		}
		
		for(Method method : updateMethods) {
			verifyNoException(method, account);
		}
	}

	private void verifyException(Class<Permissionexception> exceptionType,
			Method method, Accountable account) throws Exception {
		try {
			method.invoke(account, nullParmsFor(method));
			fail("expected exception");
		} catch (InvocationTargetException e) {
			assertEquals("expectecd exception", exceptionType, e.getCause().getClass());
		}
	}

	private Object[] nullParmsFor(Method method) {
		return new Object[method.getParameterTypes().length];
	}

	
	
	
	
	
	
	
	
	
	
	
	
	
}
